package cn.sciento.starter.tenant.custom.endpoint;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import org.apache.commons.lang3.reflect.MethodUtils;
import cn.sciento.starter.tenant.custom.annotation.TenantCustom;
import cn.sciento.starter.tenant.custom.annotation.TenantCustomPoint;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

@Component
public class CustomPointScanner implements ApplicationContextAware {
  private List<CustomPointDTO> cachePoints;
  
  private boolean init;
  
  private ApplicationContext applicationContext;
  
  private final Object lock = new Object();
  
  @Value("${spring.application.name}")
  private String serviceName;
  
  public List<CustomPointDTO> getCustomPoints() {
    if (!this.init)
      synchronized (this.lock) {
        scanCustomPoints();
        this.init = true;
      }  
    return this.cachePoints;
  }
  
  protected void scanCustomPoints() {
    List<CustomPointDTO> points = new ArrayList<>(64);
    Map<String, Object> customBeans = this.applicationContext.getBeansWithAnnotation(TenantCustom.class);
    customBeans.forEach((className, bean) -> {
          TenantCustom tenantCustom = (TenantCustom)AnnotationUtils.findAnnotation(bean.getClass(), TenantCustom.class);
          Method[] methods = MethodUtils.getMethodsWithAnnotation(bean.getClass(), TenantCustomPoint.class, true, true);
          for (Method method : methods) {
            TenantCustomPoint tenantCustomPoint = method.<TenantCustomPoint>getAnnotation(TenantCustomPoint.class);
            CustomPointDTO point = new CustomPointDTO();
            point.setCustomCode(tenantCustom.value()).setPointCode(tenantCustomPoint.value()).setDescription(tenantCustomPoint.description()).setClassName(bean.getClass().getName()).setMethodName(method.getName()).setServiceName(this.serviceName);
            points.add(point);
          } 
        });
    this.cachePoints = points;
  }
  
  public void setApplicationContext(@Nonnull ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }
}
